package com.hexinfo.dmpro.component.file.transfer.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.ftp.Ftp;
import cn.hutool.extra.ftp.FtpMode;
import com.hexinfo.dmpro.component.file.transfer.model.FileTransfer;
import com.hexinfo.dmpro.component.file.transfer.service.FileTransferService;
import com.hexinfo.dmpro.component.file.transfer.utils.FtpUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.springframework.stereotype.Service;

import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Package com.hexinfo.dmpro.component.file.transfer.service.impl
 * @ClassName FileTransferServiceImpl
 * @Description 文件传输-业务接口实现
 * @Author Wang zhihao
 * @Date 2023/9/8 17:48
 * @Version v1.0
 **/
@Slf4j
@Service
public class FileTransferServiceImpl implements FileTransferService {

    /**
     * @Method createDir
     * @Param fileTransfer
     * @Return boolean
     * @Description 创建目录
     * @Author Wang zhihao
     * @Date 2023/9/8 18:01
     * @Version V1.0
     */
    @Override
    public boolean createDir(FileTransfer fileTransfer) {
        Ftp ftp = new Ftp(fileTransfer.getServer(), fileTransfer.getPort(), fileTransfer.getUser(), fileTransfer.getPassword());
        ftp.setMode(FtpMode.Passive);
        try {
            ftp.mkDirs(fileTransfer.getRemoteDir());
            return true;
        } catch (Exception e) {
            throw new RuntimeException("【文件传输】对FTP服务器创建目录出现异常: " + e.getMessage(), e);
        } finally {
            closeFtp(ftp);
        }
    }

    /**
     * @Method fileUpload
     * @Param fileTransfer
     * @Return boolean
     * @Description 文件上传
     * @Author Wang zhihao
     * @Date 2023/9/8 18:01
     * @Version V1.0
     */
    @Override
    public boolean fileUpload(FileTransfer fileTransfer) {
        Ftp ftp = new Ftp(fileTransfer.getServer(), fileTransfer.getPort(), fileTransfer.getUser(), fileTransfer.getPassword());
        ftp.setMode(FtpMode.Passive);
        try {
            File file = new File(fileTransfer.getLocalFilePath());
            return ftp.upload(fileTransfer.getRemoteDir(), fileTransfer.getFileName(), file);
        } catch (Exception e) {
            throw new RuntimeException("【文件传输】文件上传到FTP服务器发生异常: " + e.getMessage(), e);
        } finally {
            closeFtp(ftp);
        }

    }

    /**
     * @Method fileDownload
     * @Param
     * @Return boolean
     * @Description 文件下载
     * @Author Wang zhihao
     * @Date 2023/9/8 18:01
     * @Version V1.0
     */
    @Override
    public boolean fileDownload(FileTransfer fileTransfer) {
        Ftp ftp = new Ftp(fileTransfer.getServer(), fileTransfer.getPort(), fileTransfer.getUser(), fileTransfer.getPassword());
        ftp.setMode(FtpMode.Passive);
        try {
            File file = new File(fileTransfer.getLocalFilePath());
            ftp.download(fileTransfer.getRemoteDir(), fileTransfer.getFileName(), file);
            return true;
        } catch (Exception e) {
            throw new RuntimeException("【文件传输】文件从FTP服务器下载发生异常: " + e.getMessage(), e);
        } finally {
            closeFtp(ftp);
        }
    }

    /**
     * @Method removeDir
     * @Param fileTransfer
     * @Return boolean
     * @Description 删除目录
     * @Author Wang zhihao
     * @Date 2023/9/8 18:01
     * @Version V1.0
     */
    @Override
    public boolean removeDir(FileTransfer fileTransfer) {
        Ftp ftp = new Ftp(fileTransfer.getServer(), fileTransfer.getPort(), fileTransfer.getUser(), fileTransfer.getPassword());
        ftp.setMode(FtpMode.Passive);
        try {
            return ftp.delDir(fileTransfer.getRemoteDir());
        } catch (Exception e) {
            throw new RuntimeException("【文件传输】从FTP服务器删除目录发生异常: " + e.getMessage(), e);
        } finally {
            closeFtp(ftp);
        }
    }

    /**
     * @Method getDirSize
     * @Param fileTransfer
     * @Return double
     * @Description 计算文件/目录存储大小
     * @Author Yin.Yang
     * @Date 2023/12/26 17:50
     * @Version V1.0
     */
    @Override
    public double getDirSize(FileTransfer fileTransfer) {
        try (Ftp ftp = new Ftp(fileTransfer.getServer(), fileTransfer.getPort(), fileTransfer.getUser(), fileTransfer.getPassword())) {
            ftp.setMode(FtpMode.Passive);
            return calculateSize(ftp, fileTransfer.getRemoteDir());
        } catch (Exception e) {
            throw new RuntimeException("【文件传输】获取目录大小发生异常: " + e.getMessage(), e);
        }
    }

    private double calculateSize(Ftp ftp, String path) {
        double size = 0;
        try {
            List<FTPFile> files = listFiles(ftp, path);
            for (FTPFile file : files) {
                if (!file.isDirectory()) { // 只计算文件的大小
                    size += file.getSize();
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("【文件传输】计算目录大小时发生错误: " + e.getMessage(), e);
        }
//        return NumberUtil.round(size / (1024.0 * 1024.0 * 1024.0), 2).doubleValue();
        return NumberUtil.round(size, 2).doubleValue();
    }


    @Override
    public void removeDir(FTPClient ftpClient, String dir) {
        try {
            ftpClient.removeDirectory(dir);
        } catch (Exception e) {
            log.error("【文件传输】从FTP服务器删除目录发生异常: {}", e.getMessage(), e);
            throw new RuntimeException("【文件传输】从FTP服务器删除目录发生异常:" + e.getMessage(), e);
        }
    }

    /**
     * @param fileTransfer
     * @Method removeFile
     * @Param
     * @Return boolean
     * @Description 删除文件
     * @Author Wang zhihao
     * @Date 2023/9/11 14:26
     * @Version V1.0
     */
    @Override
    public boolean removeFile(FileTransfer fileTransfer) {
        Ftp ftp = new Ftp(fileTransfer.getServer(), fileTransfer.getPort(), fileTransfer.getUser(), fileTransfer.getPassword());
        ftp.setMode(FtpMode.Passive);
        try {
            return ftp.delFile(fileTransfer.getRemoteFilePath());
        } catch (Exception e) {
            throw new RuntimeException("【文件传输】从FTP服务器删除文件发生异常: " + e.getMessage(), e);
        } finally {
            closeFtp(ftp);
        }
    }

    /**
     * @param ftpClient
     * @param filePath
     * @Method removeFile
     * @Param
     * @Return boolean
     * @Description 通过FTP删除文件
     * @Author Wang zhihao
     * @Date 2023/10/19 19:09
     * @Version V1.0
     */
    @Override
    public boolean removeFile(FTPClient ftpClient, String filePath) {
        try {
            FTPFile[] remoteFiles = ftpClient.listFiles(filePath);
            if (remoteFiles.length > 0) {
                ftpClient.deleteFile(filePath);
                return true;
            } else {
                throw new RuntimeException("【文件传输】删除文件异常: 该文件不存在");
            }
        } catch (Exception e) {
            throw new RuntimeException("【文件传输】删除文件异常:" + e.getMessage(), e);
        }
    }

    /**
     * @param ftp
     * @Method closeFtp
     * @Param
     * @Return void
     * @Description 公共关闭FTP方法
     * @Author Wang zhihao
     * @Date 2023/9/11 20:04
     * @Version V1.0
     */
    private void closeFtp(Ftp ftp) {
        try {
            ftp.close();
        } catch (Exception e) {
            log.error("【文件传输】FTP关闭失败，请检查异常情况");
        }
    }

    /**
     * @Method fileTransfer
     * @Param fileTransfer
     * @Return boolean
     * @Description 文件备份
     * @Author Wang zhihao
     * @Date 2023/9/8 18:01
     * @Version V1.0
     */
    @Override
    public boolean fileTransfer(FileTransfer fileTransfer) {
        try {
            FTPClient ftpClient = FtpUtil.getFtpClient(fileTransfer.getServer(), fileTransfer.getPort(), fileTransfer.getUser(), fileTransfer.getPassword());
            ftpClient.rename(fileTransfer.getRemoteFilePath(), fileTransfer.getRemoteFileNewPath());
            ftpClient.disconnect();
            return true;
        } catch (Exception e) {
            throw new RuntimeException("【文件传输】文件备份发生异常: " + e.getMessage(), e);
        }
    }

    /**
     * @param fileTransfer
     * @Method isFolderExists
     * @Param
     * @Return boolean
     * @Description 检测文件目录是否存在
     * @Author Wang zhihao
     * @Date 2023/10/18 14:29
     * @Version V1.0
     */
    @Override
    public boolean isFolderExists(FileTransfer fileTransfer) {
        FTPClient ftpClient = null;
        try {
            ftpClient = FtpUtil.getFtpClient(fileTransfer.getServer(), fileTransfer.getPort(), fileTransfer.getUser(), fileTransfer.getPassword());
            int replyCode = ftpClient.getReplyCode();
            if (!FTPReply.isPositiveCompletion(replyCode)) {
                ftpClient.disconnect();
                return false;
            }
            ftpClient.changeWorkingDirectory(fileTransfer.getRemoteDir());
            replyCode = ftpClient.getReplyCode();
            if (replyCode == 550) {
                return false;
            }
            return true;
        } catch (Exception e) {
            throw new RuntimeException("【文件传输】文件存在性检查发生异常: " + e.getMessage(), e);
        } finally {
            if (ftpClient.isConnected()) {
                try {
                    ftpClient.disconnect();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * @param fileTransfer
     * @Method isFileExists
     * @Param
     * @Return boolean
     * @Description 检测文件是否存在
     * @Author Wang zhihao
     * @Date 2023/10/18 14:31
     * @Version V1.0
     */
    @Override
    public boolean isFileExists(FileTransfer fileTransfer) {
        FTPClient ftpClient = null;
        try {
            ftpClient = FtpUtil.getFtpClient(fileTransfer.getServer(), fileTransfer.getPort(), fileTransfer.getUser(), fileTransfer.getPassword());
            int replyCode = ftpClient.getReplyCode();
            if (!FTPReply.isPositiveCompletion(replyCode)) {
                ftpClient.disconnect();
                return false;
            }
            FTPFile[] remoteFiles = ftpClient.listFiles(fileTransfer.getRemoteFilePath());
            if (remoteFiles.length > 0) {
                return true;
            } else {
                return false;
            }
        } catch (Exception e) {
            throw new RuntimeException("【文件传输】文件存在性检查发生异常: " + e.getMessage(), e);
        } finally {
            if (ftpClient.isConnected()) {
                try {
                    ftpClient.disconnect();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * @param fileTransfer
     * @Method fileTransferUseClient
     * @Param
     * @Return void
     * @Description 用入参FTPClient备份文件
     * @Author Wang zhihao
     * @Date 2023/10/19 14:12
     * @Version V1.0
     */
    @Override
    public void fileRenameUseClient(FileTransfer fileTransfer) {
        try {
            FTPClient ftpClient = fileTransfer.getFtpClient();
            ftpClient.rename(fileTransfer.getRemoteFilePath(), fileTransfer.getRemoteFileNewPath());
        } catch (Exception e) {
            throw new RuntimeException("【文件传输】根据FTP对远程服务器文件做备份出现异常: " + e.getMessage(), e);
        }
    }

    @Override
    public void fileRename(FTPClient ftpClient, String originalFilePath, String newFilePath) {
        try {
            ftpClient.rename(originalFilePath, newFilePath);
        } catch (Exception e) {
            throw new RuntimeException("【文件传输】根据FTP对远程服务器文件重命名出现异常: " + e.getMessage(), e);
        }
    }

    /**
     * @Method fileBackup
     * @Param
     * @param ftpClient
     * @param originalFilePath
     * @param newFileDir
     * @param fileName
     * @Return void
     * @Description 文件备份
     * @Author Wang zhihao
     * @Date 2023/11/8 16:20
     * @Version V1.0
     */
    @Override
    public void fileBackup(FTPClient ftpClient, String originalFilePath, String newFileDir, String fileName) {
        try {
            boolean isDirExist = ftpClient.changeWorkingDirectory(newFileDir);
            if (!isDirExist) {
                String[] dirs = newFileDir.split("/");
                for (String dir : dirs) {
                    if (StrUtil.isNotEmpty(dir)) {
                        ftpClient.makeDirectory(dir);
                        ftpClient.changeWorkingDirectory(dir);
                    }
                }
            }
            ftpClient.rename(originalFilePath, newFileDir + "/" + fileName);
        } catch (Exception e) {
            log.error("【文件传输】根据FTP对远程服务器文件做备份出现异常: {}", e.getMessage(), e);
            throw new RuntimeException("【文件传输】根据FTP对远程服务器文件做备份出现异常: {}" + e.getMessage(), e);
        }

    }

    /**
     * @param fileTransfer
     * @Method createDirUseClient
     * @Param
     * @Return void
     * @Description 用入参FTPClient创建文件目录
     * @Author Wang zhihao
     * @Date 2023/10/19 14:12
     * @Version V1.0
     */
    @Override
    public void createDirUseClient(FileTransfer fileTransfer) {
        try {
            FTPClient ftpClient = fileTransfer.getFtpClient();
            ftpClient.makeDirectory(fileTransfer.getRemoteDir());
        } catch (Exception e) {
            throw new RuntimeException("【文件传输】根据FTP对远程服务器创建目录出现异常: " + e.getMessage(), e);
        }

    }

    /**
     * @param fileTransfer
     * @Method isFolderExistsUseClient
     * @Param
     * @Return boolean
     * @Description 用入参FTPClient检测文件目录是否存在
     * @Author Wang zhihao
     * @Date 2023/10/19 13:44
     * @Version V1.0
     */
    @Override
    public boolean isFolderExistsUseClient(FileTransfer fileTransfer) {
        try {
            FTPClient ftpClient = fileTransfer.getFtpClient();
            ftpClient.changeWorkingDirectory(fileTransfer.getRemoteDir());
            int replyCode = ftpClient.getReplyCode();
            if (replyCode == 550) {
                return false;
            }
            return true;
        } catch (Exception e) {
            throw new RuntimeException("【文件传输】文件存在性检查发生异常: " + e.getMessage(), e);
        }
    }

    /**
     * @param fileTransfer
     * @Method isFileExistsUseClient
     * @Param
     * @Return boolean
     * @Description 用入参FTPClient检测文件是否存在
     * @Author Wang zhihao
     * @Date 2023/10/19 13:45
     * @Version V1.0
     */
    @Override
    public boolean isFileExistsUseClient(FileTransfer fileTransfer) {
        FTPClient ftpClient = fileTransfer.getFtpClient();
        try {
            FTPFile[] remoteFiles = ftpClient.listFiles(fileTransfer.getRemoteFilePath());
            if (remoteFiles.length > 0) {
                return true;
            } else {
                return false;
            }
        } catch (Exception e) {
            throw new RuntimeException("【文件传输】文件存在性检查发生异常: " + e.getMessage(), e);
        }
    }

    @Override
    public boolean isFileExistsUseClient(FTPClient ftpClient, String remoteFilePath) {
        try {
            FTPFile[] remoteFiles = ftpClient.listFiles(remoteFilePath);
            if (remoteFiles.length > 0) {
                return true;
            } else {
                return false;
            }
        } catch (Exception e) {
            log.error("【文件传输】文件存在性检查发生异常: {}", e.getMessage(), e);
            e.printStackTrace();
            return false;
        }
    }

    /**
     * @param fileTransfer
     * @Method fileUploadWithCreate
     * @Param
     * @Return void
     * @Description 用入参FTPClient上传文件(若目录不存在会创建)
     * @Author Wang zhihao
     * @Date 2023/10/19 14:09
     * @Version V1.0
     */
    @Override
    public void fileUploadUseClient(FileTransfer fileTransfer) {
        try {
            FTPClient ftpClient = fileTransfer.getFtpClient();
            boolean isDirExist = ftpClient.changeWorkingDirectory(fileTransfer.getRemoteDir());
            if (!isDirExist) {
                ftpClient.makeDirectory(fileTransfer.getRemoteDir());
                ftpClient.changeWorkingDirectory(fileTransfer.getRemoteDir());
            }
            InputStream in = FileUtil.getInputStream(new File(fileTransfer.getLocalFilePath()));
            ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
            ftpClient.storeFile(fileTransfer.getFileName(), in);
        } catch (Exception e) {
            throw new RuntimeException("【文件传输】文件上传发生异常" + e.getMessage(), e);
        }
    }

    @Override
    public void fileUploadUseClient(FTPClient ftpClient, String remoteDir, String localFilePath, String fileName) {
        try {
            boolean isDirExist = ftpClient.changeWorkingDirectory(remoteDir);
            if (!isDirExist) {
                String[] dirs = remoteDir.split("/");
                for (String dir : dirs) {
                    if (StrUtil.isNotEmpty(dir)) {
                        ftpClient.makeDirectory(dir);
                        ftpClient.changeWorkingDirectory(dir);
                    }
                }
            }
            InputStream in = FileUtil.getInputStream(new File(localFilePath));
            ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
            ftpClient.storeFile(fileName, in);
        } catch (Exception e) {
            log.error("【文件传输】文件上传发生异常: {}", e.getMessage(), e);
            throw new RuntimeException("【文件传输】文件上传发生异常" + e.getMessage(), e);
        }
    }

    /**
     * @param ftpClient
     * @param fileDir
     * @Method getFileByDir
     * @Param
     * @Return java.util.List<java.lang.String>
     * @Description
     * @Author Wang zhihao
     * @Date 2023/10/19 17:23
     * @Version V1.0
     */
    @Override
    public List<String> getFileByDir(FTPClient ftpClient, String fileDir) {
        try {
            boolean isDirExist = ftpClient.changeWorkingDirectory(fileDir);
            if (!isDirExist) {
                log.error("【FTP连接】该目录不存在，请检查");
                throw new RuntimeException("【FTP连接】该目录不存在，请检查");
            } else {
                return Arrays.stream(ftpClient.listFiles(fileDir)).map(FTPFile::getName).collect(Collectors.toList());
            }
        } catch (Exception e) {
            throw new RuntimeException("【文件传输】获取指定目录下文件异常：" + e.getMessage(), e);
        }
    }

    /*
     * @Method: listFiles <br>
     * @Param: [fileTransfer] <br>
     * @Return: java.util.List<java.lang.String> <br>
     * @Description：获取指定目录下所有文件<br>
     * @Author： wz.li<br>
     * @Date： 2023/11/9 17:42 <br>
     * @Version： V2.0.2<br>
     */
    @Override
    public List<FTPFile> listFiles(Ftp ftpClient, String fileDir) throws Exception {
        List<FTPFile> fileList = new ArrayList<>();
        try {
            boolean isDirExist = ftpClient.exist(fileDir);
            if (!isDirExist) {
                log.error("【FTP连接】该目录不存在，请检查");
                return fileList;
            } else {
                fileDir = fileDir.endsWith("/") ? fileDir : fileDir + "/";
                return traverseRemoteDirectory(ftpClient, fileDir);
            }
        } catch (Exception e) {
            throw new Exception("【文件传输】获取指定目录下所有文件异常：" + e.getMessage(), e);
        }
    }

    /*
     * @Method: readFile <br>
     * @Param: [fileTransfer] <br>
     * @Return: java.lang.String <br>
     * @Description：读取FTP文件内容<br>
     * @Author： wz.li<br>
     * @Date： 2023/11/22 14:24 <br>
     * @Version： V2.0.2<br>
     */
    @Override
    public String readFile(FileTransfer fileTransfer) {
        FTPClient ftpClient = new FTPClient();

        try {
            ftpClient.connect(fileTransfer.getServer(), fileTransfer.getPort());
            ftpClient.login(fileTransfer.getUser(), fileTransfer.getPassword());
            ftpClient.enterLocalPassiveMode();
            // 设置FTP传输模式为二进制
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
            // 切换到FTP服务器的目录
            ftpClient.changeWorkingDirectory(fileTransfer.getRemoteDir());
            // 获取指定的TXT文件
            InputStream inputStream = ftpClient.retrieveFileStream(fileTransfer.getFileName());
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            String line;
            StringBuilder fileContent = new StringBuilder();
            while ((line = reader.readLine()) != null) {
                fileContent.append(line);
            }
            reader.close();
            ftpClient.completePendingCommand();
            return fileContent.toString();
        } catch (IOException e) {
            throw new RuntimeException("【文件传输】读取文件异常：" + e.getMessage(), e);
        } finally {
            closeFtp(ftpClient);
        }
    }

    private void closeFtp(FTPClient ftpClient) {
        try {
            if (ftpClient.isConnected()) {
                ftpClient.logout();
                ftpClient.disconnect();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /*
     * @Method: traverseRemoteDirectory <br>
     * @Param: [ftpClient, remoteDirPath] <br>
     * @Return: java.util.List<java.lang.String> <br>
     * @Description：递归遍历子目录<br>
     * @Author： wz.li<br>
     * @Date： 2023/11/7 16:46 <br>
     * @Version： V2.0.2<br>
     */
    private List<FTPFile> traverseRemoteDirectory(Ftp ftpClient, String remoteDirPath) {
        List<FTPFile> filePathList = new ArrayList<>();
        FTPFile[] files = ftpClient.lsFiles(remoteDirPath);
        for (FTPFile file : files) {
            if (file.isDirectory()) {
                filePathList.addAll(traverseRemoteDirectory(ftpClient, remoteDirPath + "/" + file.getName()));
            } else {
                filePathList.add(file);
            }
        }
        return filePathList;
    }

}
